#include "./cmd_uart.h"
#include "../device/uart.h"
#include "../conn/conn_type.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

namespace driver {

using namespace device::uart;
using namespace utils;
using namespace conn;

using TX = PA<9>; // DMA1_Channel4
using TX_DMA = Dma<DMA1_BASE, 4>;
using TX_DMA_CHAN = DmaChan<DMA1_Channel4_BASE>;

using RX = PA<10>; // DMA1_Channel5
using RX_DMA = Dma<DMA1_BASE, 5>;
using RX_DMA_CHAN = DmaChan<DMA1_Channel5_BASE>;

static constexpr uint32_t MyBrr = 72000000 / 115200;

using MY_UART = Uart<USART1_BASE, MyBrr, TX, RX, TX_DMA, TX_DMA_CHAN, RX_DMA, RX_DMA_CHAN>;

CmdUart::CmdUart() {

    notify_write_complete();
    wait_ack_unlock();

    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    MY_UART::init(m_recv_buf, RECV_BUF_SIZE);

    NVIC_EnableIRQ(USART1_IRQn);
    NVIC_EnableIRQ(DMA1_Channel4_IRQn);
}

void CmdUart::write_data(const ConnInfo &conn_info, const void *buf, uint8_t data_len) {
    wait_write_complete();
    memcpy(m_write_buf + 6, buf, data_len);
    uint8_t len = conn::proto::set_rs232_head(m_write_buf, data_len, conn_info);
    write_by_dma(m_write_buf, len);
}

void CmdUart::write_ack(uint8_t dest, uint8_t seq) {
    wait_write_complete();
    encode(m_write_buf, proto::HEAD);
    m_write_buf[2] = 8;
    m_write_buf[3] = proto::REQ_ACK;
    m_write_buf[4] = (seq << 4) + ConnInfo::TYPE_RESPONSE;
    m_write_buf[5] = (MY_ADDR << 4) + dest;
    m_write_buf[6] = 0;
    m_write_buf[7] = proto::END;
    write_by_dma(m_write_buf, 8);
}

const ConnInfo CONN_LOG(proto::REQ_LOG, 0, ConnInfo::TYPE_NOTIFY, MY_ADDR, addr::IPC);

void CmdUart::printf(const char *format, ...) {
    va_list ap;
    va_start(ap, format);
    wait_write_complete();
    int n = vsprintf(reinterpret_cast<char *>(m_write_buf + 7), format, ap);
    m_write_buf[6] = n;
    uint8_t len = conn::proto::set_rs232_head(m_write_buf, 1 + n, CONN_LOG);
    write_by_dma(m_write_buf, len);
}

Error CmdUart::write_data_wait_ack(const ConnInfo &conn_info, const void *buf, uint8_t data_len) {
    Error err;
    wait_ack_lock();
    clear_ack();
    for (uint8_t i = 0; i < 3; i ++) {
        write_data(conn_info, buf, data_len);
        if (wait_ack()) {
            goto finish;
        }
    }
    err = ec::RS232_NOT_RECV_ACK;
finish:
    wait_ack_unlock();
    return err;
}

void CmdUart::write_by_dma(const void *buf, uint8_t len) {
    MY_UART::write(buf, len);
}

void CmdUart::on_recv_idle() {
    uint32_t index = RECV_BUF_SIZE - RX_DMA_CHAN::dntr();
    if (index == m_recv_index) {
        return;
    }
    utils::ByteView bw;
    if (index > m_recv_index) {
        bw.buf = m_recv_buf + m_recv_index;
        bw.len = index - m_recv_index;
    } else {
        uint32_t k = RECV_BUF_SIZE - m_recv_index;
        memcpy(m_copy_buf, m_recv_buf + m_recv_index, k);
        memcpy(m_copy_buf + k, m_recv_buf, index);
        bw.buf = m_copy_buf;
        bw.len = index + k;
    }
    m_mq.post(bw);
    m_recv_index = index;
}



}

using namespace driver;

extern "C"
void USART1_IRQHandler() {
    if (!MY_UART::is_idle()) {
        return;
    }
    MY_UART::clear_idle_flag();
    cmdUart->on_recv_idle();
}

extern "C"
void DMA1_Channel4_IRQHandler() {
    if (!TX_DMA::is_complete()) {
        return;
    }
    TX_DMA::clear_flags();
    cmdUart->notify_write_complete();
}

